package za.ac.sun.cs.viz.distrib;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.geom.Rectangle2D;

import javax.swing.JComponent;

import org.abego.treelayout.TreeForTreeLayout;
import org.abego.treelayout.TreeLayout;
import org.abego.treelayout.util.DefaultConfiguration;

@SuppressWarnings("serial")
public class VizTreePane extends JComponent {

	private TreeLayout<VizNode> treeLayout;

	private final DefaultConfiguration<VizNode> configuration;

	private final VizExtentProvider nodeExtentProvider;

	private static Font font = null;

	private TreeForTreeLayout<VizNode> getTree() {
		return treeLayout.getTree();
	}

	private Iterable<VizNode> getChildren(VizNode parent) {
		return getTree().getChildren(parent);
	}

	private Rectangle2D.Double getBoundsOfNode(VizNode node) {
		return treeLayout.getNodeBounds().get(node);
	}
	
	/**
	 * Specifies the tree to be displayed by passing in a {@link TreeLayout} for
	 * that tree.
	 * 
	 * @param treeLayout
	 */
	public VizTreePane(TreeForTreeLayout<VizNode> tree) {
		double gapBetweenLevels = 50;
		double gapBetweenNodes = 10;
		configuration = new DefaultConfiguration<VizNode>(gapBetweenLevels, gapBetweenNodes);
		nodeExtentProvider = new VizExtentProvider(this);
		treeLayout = new TreeLayout<VizNode>(tree, nodeExtentProvider, configuration);
		Dimension size = treeLayout.getBounds().getBounds().getSize();
		setPreferredSize(size);
		setSize(size);
	}

	public void update(TreeForTreeLayout<VizNode> tree) {
		determineSizes(tree);
		treeLayout = new TreeLayout<VizNode>(tree, nodeExtentProvider, configuration);
		Dimension size = treeLayout.getBounds().getBounds().getSize();
		setPreferredSize(size);
		setSize(size);
		repaint();
	}

	private void determineSizes(TreeForTreeLayout<VizNode> tree) {
		determineSize(tree, tree.getRoot());
	}

	private void determineSize(TreeForTreeLayout<VizNode> tree, VizNode node) {
		node.setHW(this);
		for (VizNode child : tree.getChildren(node)) {
			determineSize(tree, child);
		}
	}

	// -------------------------------------------------------------------
	// painting

	public final static int NOONE = -1;
	
	public final static int INACTIVE = 0;
	public final static int NEW = 1;
	public final static int YES = 2;
	public final static int NO = 3;
	public final static int CHANGE = 4;

	private final static int ARC_SIZE = 10;
	private final static Color[] BOX_COLORS = { Color.lightGray, Color.cyan, Color.green, Color.red, Color.orange };
	private final static Color BORDER_COLOR = Color.darkGray;
	private final static Color TEXT_COLOR = Color.black;

	private void paintEdges(Graphics g, VizNode parent) {
		if (!getTree().isLeaf(parent)) {
			Rectangle2D.Double b1 = getBoundsOfNode(parent);
			double x1 = b1.getCenterX();
			double y1 = b1.getCenterY();
			for (VizNode child : getChildren(parent)) {
				Rectangle2D.Double b2 = getBoundsOfNode(child);
				g.drawLine((int) x1, (int) y1, (int) b2.getCenterX(),
						(int) b2.getCenterY());

				paintEdges(g, child);
			}
		}
	}

	private void paintBox(Graphics g, VizNode vizNode) {
		if (font == null) {
			Font f = getFont();
			font = new Font(f.getName(), f.getStyle(), 11);
		}
		setFont(font);
		int a = vizNode.getActivity();
		if ((a >= 0) && (a < BOX_COLORS.length)) {
			g.setColor(BOX_COLORS[a]);
		} else {
			g.setColor(BOX_COLORS[0]);
		}
		Rectangle2D.Double box = getBoundsOfNode(vizNode);
		g.fillRoundRect((int) box.x, (int) box.y, (int) box.width - 1,
				(int) box.height - 1, ARC_SIZE, ARC_SIZE);
		g.setColor(BORDER_COLOR);
		g.drawRoundRect((int) box.x, (int) box.y, (int) box.width - 1,
				(int) box.height - 1, ARC_SIZE, ARC_SIZE);
		g.setColor(TEXT_COLOR);
		String[] lines = vizNode.getText().split("\n");
		FontMetrics m = getFontMetrics(getFont());
		int x = (int) box.x + ARC_SIZE / 2;
		int y = (int) box.y + m.getAscent() + m.getLeading() + 1;
		for (int i = 0; i < lines.length; i++) {
			g.drawString(lines[i], x, y);
			y += m.getHeight();
		}
		int i = vizNode.getID();
		if (i >= 0) {
			int xx = (int) (box.x + box.width - 7);
			int yy = (int) (box.y - 8);
			g.setColor(Color.white);
			g.fillOval(xx, yy, 15, 15);
			g.setColor(Color.black);
			g.drawOval(xx, yy, 15, 15);
			g.drawString(Integer.toString(i), xx + 5, yy + 11);			
		}
	}

	@Override
	public void paint(Graphics g) {
		super.paint(g);
		paintEdges(g, getTree().getRoot());
		for (VizNode textInBox : treeLayout.getNodeBounds().keySet()) {
			paintBox(g, textInBox);
		}
	}

}
